﻿using UnityEngine;
using System.Collections.Generic;
using System;
using DaVikingCode.RectanglePacking;
using System.IO;

/// <summary>
/// 参考资料:http://www.xuanyusong.com/archives/4535
/// 图集动态合并
/// @lysz
/// </summary>
public class TextureCombine
{
    private RectanglePacker mPacker;

    public Texture2D CombinedTex;
    public int CombinedTexSize = 1024;
    private byte[] data = null;

#if UNITY_ANDROID
    public TextureFormat CombinedTexFormat = TextureFormat.ETC2_RGBA8;
#elif UNITY_IOS
    public TextureFormat CombinedTexFormat = TextureFormat.PVRTC_RGBA4;
#elif UNITY_STANDALONE
    public TextureFormat CombinedTexFormat = TextureFormat.DXT5;
#endif

    public IntegerRectangle tmpRectangle;

    Dictionary<string, S_DynamicAtlasSheet> mSheedDic = new Dictionary<string, S_DynamicAtlasSheet>();

    public S_DynamicAtlasSheet GetSheetData(string spName)
    {
        if (CombinedTex == null)
            return default(S_DynamicAtlasSheet);

        S_DynamicAtlasSheet spData;

        mSheedDic.TryGetValue(spName, out spData);

        return spData;
    }

    public S_DynamicAtlasSheet Combine(byte[] texData, string spName)
    {
        if (null == texData)
            return default(S_DynamicAtlasSheet);

        if (CombinedTex == null)
        {
            mPacker = new RectanglePacker(CombinedTexSize, CombinedTexSize, 0);
            CombinedTex = new Texture2D(CombinedTexSize, CombinedTexSize, CombinedTexFormat, false);
        }

        S_DynamicAtlasSheet spData = new S_DynamicAtlasSheet();

        if (tmpRectangle == null)
            tmpRectangle = new IntegerRectangle();

        int oldOackerNum = mPacker.rectangleCount;

        MemoryStream stream = new MemoryStream(texData);
        byte[] wb = new byte[4];
        byte[] wh = new byte[4];
        byte[] rawData = new byte[texData.Length - 8];
        stream.Read(wb, 0, 4);
        stream.Read(wh, 0, 4);
        stream.Read(rawData, 0, rawData.Length);

        int tWidth = System.BitConverter.ToInt32(wb, 0);
        int tHeight = System.BitConverter.ToInt32(wh, 0);

        mPacker.insertRectangle(tWidth, tHeight, spName);
        mPacker.packRectangles();

        int curOackerNum = mPacker.rectangleCount;

        if (oldOackerNum == curOackerNum)
            return default(S_DynamicAtlasSheet);

        mPacker.getRectangle(spName, tmpRectangle);

        int length = CombinedTexSize;
        var blcokBytes = 0;
        switch (CombinedTexFormat)
        {
            case TextureFormat.DXT1:
            case TextureFormat.ETC_RGB4:
            case TextureFormat.ETC2_RGB:
                blcokBytes = 8;
                if (data == null)
                {
                    data = new byte[length / 2 * length];
                }
                break;
            case TextureFormat.DXT5:
            case TextureFormat.ETC2_RGBA8:
            case TextureFormat.ASTC_RGB_4x4:
            case TextureFormat.ASTC_RGBA_4x4:
                blcokBytes = 16;
                if (data == null)
                {
                    data = new byte[length * length];
                }
                break;
            default:
                UnityEngine.Debug.Log("Not supported.");
                return default(S_DynamicAtlasSheet);
        }

        CombineBlocks(rawData, data, tmpRectangle.x, tmpRectangle.y, tmpRectangle.width, tHeight, 4, blcokBytes, length);

        CombinedTex.LoadRawTextureData(data);
        CombinedTex.Apply();

        spData.Name = spName;
        spData.x = tmpRectangle.x;
        //spData.y = CombinedTexSize - tmpRectangle.y - tmpRectangle.height; //ngui
        spData.y = tmpRectangle.y;
        spData.width = tmpRectangle.width;
        spData.height = tmpRectangle.height;
        spData.MainTexture = CombinedTex;

        //spData.Texture = Sprite.Create(CombinedTex, new Rect(spData.x, spData.y, spData.width, spData.height), Vector2.zero).texture;
        spData.Texture = new Texture2D(spData.width, spData.height, CombinedTexFormat, false);

        spData.Texture.Apply();

        if (SystemInfo.copyTextureSupport.HasFlag(UnityEngine.Rendering.CopyTextureSupport.Basic))
            Graphics.CopyTexture(CombinedTex, 0, 0, spData.x, spData.y, spData.width, spData.height, spData.Texture, 0, 0, 0, 0);
        else
            Debug.LogError("SystemInfo.copyTextureSupport dont supported!");

        mSheedDic.Add(spName, spData);

        return spData;
    }

    public S_DynamicAtlasSheet Combine(Texture2D tex)
    {
        if (null == tex)
            return default(S_DynamicAtlasSheet);

        if (CombinedTex == null)
        {
            mPacker = new RectanglePacker(CombinedTexSize, CombinedTexSize,0);
            CombinedTex = new Texture2D(CombinedTexSize, CombinedTexSize, CombinedTexFormat, false);
        }

        string spName = tex.name;

        S_DynamicAtlasSheet spData = new S_DynamicAtlasSheet();

        if (tmpRectangle == null)
            tmpRectangle = new IntegerRectangle();

        int oldOackerNum = mPacker.rectangleCount;

        int tWidth = tex.width;
        int tHeight = tex.height;

        mPacker.insertRectangle(tWidth, tHeight, tex.name);
        mPacker.packRectangles();

        int curOackerNum = mPacker.rectangleCount;

        if (oldOackerNum == curOackerNum)
            return default(S_DynamicAtlasSheet);

        mPacker.getRectangle(spName, tmpRectangle);

        int length = CombinedTexSize;
        var blcokBytes = 0;
        switch (CombinedTexFormat)
        {
            case TextureFormat.DXT1:
            case TextureFormat.ETC_RGB4:
            case TextureFormat.ETC2_RGB:
                blcokBytes = 8;
                if(data == null)
                {
                    data = new byte[length / 2 * length];
                }
                break;
            case TextureFormat.DXT5:
            case TextureFormat.ETC2_RGBA8:
            case TextureFormat.ASTC_RGB_4x4:
            case TextureFormat.ASTC_RGBA_4x4:
                blcokBytes = 16;
                if (data == null)
                {
                    data = new byte[length * length];
                }
                break;
            default:
                UnityEngine.Debug.Log("Not supported.");
                return default(S_DynamicAtlasSheet);
        }

        CombineBlocks(tex.GetRawTextureData(), data, tmpRectangle.x, tmpRectangle.y, tmpRectangle.width, tHeight, 4, blcokBytes, length);

        CombinedTex.LoadRawTextureData(data);
        CombinedTex.Apply();

        spData.Name = spName;
        spData.x = tmpRectangle.x;
        //spData.y = CombinedTexSize - tmpRectangle.y - tmpRectangle.height;
        spData.y = tmpRectangle.y;
        spData.width = tmpRectangle.width;
        spData.height = tmpRectangle.height;
        spData.MainTexture = CombinedTex;
        //spData.Texture = Sprite.Create(CombinedTex, new Rect(spData.x, spData.y, spData.width, spData.height), Vector2.zero).texture;
        spData.Texture = new Texture2D(spData.width, spData.height, CombinedTexFormat,false);


        spData.Texture.Apply();

        Graphics.CopyTexture(CombinedTex, 0, 0,spData.x, spData.y, spData.width, spData.height, spData.Texture, 0, 0, 0, 0);

        mSheedDic.Add(spName, spData);

        return spData;
    }

    void CombineBlocks(byte[] src, byte[] dst, int dstx, int dsty, int width,int height, int block, int bytes, int length)
    {
        float beiNum = 0.25f;
        int dstbx = (int)(dstx * beiNum);
        int dstby = (int)(dsty * beiNum);
        int lenW = (int)(width * beiNum);
        int lenH = (int)(height * beiNum);
        int len2 = (int)(length * beiNum);

        int lenbytes = lenW * bytes;
        for (int i = 0; i < lenH; i++)
        {
            int dstindex = (dstbx + (dstby + i) * (len2)) * bytes;
            int srcindex = i * lenbytes;

            Buffer.BlockCopy(src, srcindex, dst, dstindex, lenbytes);
        }
    }
}